#include <iostream>
#include <string>
#include <ncurses.h>
#include <fstream>
#include <sys/wait.h>
#include <sqlite3.h>
#include <fstream>
#include <map>
#include <stack>


#include "Database.h"
#include "Query.h"
#include "Feed.h"
#include "RssFeed.h"
#include "AtomFeed.h"
#include "Post.h"
#include "Request.h"
#include "FreshNet.h"

using namespace std;

enum Parameter
{
    PARAM_ADD,
    PARAM_REMOVE,
    PARAM_DATABASE,
    PARAM_LIST,
    PARAM_POSTS,
    PARAM_UPDATE
};

static void addFeed(string url, FreshNet & freshnet, bool checkExists = true)
{
    if(checkExists && freshnet.containsFeed(url))
    {
        cout << "feed \"" << url << "\" has already been added" << endl; 
        return;
    }
    
    Request request(url);
    string xml = request.GetResponse();
    
    Feed* feed = NULL;
    
    if(xml.find("<channel>") != string::npos)
    {
        cout << "RSS feed found!" << endl;
        feed = new RssFeed(RssFeed::FromXml(xml));
    }
    else if(xml.find("http://www.w3.org/2005/Atom") != string::npos)
    {
        cout << "Atom feed found!" << endl;
        feed = new AtomFeed(AtomFeed::FromXml(xml));
    }
   
    
    if(feed == NULL)
    {
        cout << "Error adding feed \"" << url << "\"" << endl;
        return;
    }
    
    feed->setFeedUrl(url);
    
    string title = feed->getTitle();
    string description = feed->getDescription();
    string author = feed->getAuthor();
    string pageUrl = feed->getPageUrl();
    cout << "The following feed has been added:" << endl;
    if(title != "")
        cout << "Title: " << title << endl;
    if(description != "")
        cout << "Description: " << description << endl;
    if(author != "")
        cout << "Author: " << author << endl;
    if(pageUrl != "")
        cout << "Web Site: " << pageUrl << endl;
    cout << "Feed URL: " << url << endl;  
      
    freshnet.addFeed(*feed);
    
    delete feed;    
}

static void removeFeed(string url, FreshNet & freshnet)
{
    if(!freshnet.containsFeed(url))
    {
        cout << "The feed \"" << url << "\" has not been added in FreshNet" << endl;
        return;
    }
    
    freshnet.removeFeed(url);
}

static void assertExtraParameter(int argc, int i)
{
    if (i >= argc - 1) {
	FreshNet::printUsage();
	exit(1);
    }
}

static const char* printFeed(void* feedPtr)
{
    Feed* feed = (Feed*) feedPtr;
    if(feed->getTitle() != "")
        return feed->getTitle().c_str();
    else
        return feed->getFeedUrl().c_str();
}

static const char* printPost(void* postPtr)
{
    Post* post = (Post*) postPtr;
    
    if(post->title == "")
        return post->description.c_str();
    else 
        return post->title.c_str();
}

static void drawTitle()
{
    box(stdscr, 0, 0);
    mvwprintw(stdscr, 0, COLS/2 - 6, "%s", " FreshNet v0.1 ");
}

static void drawListBox(vector<void*>& items, const char* printFunc(void*), int starty, void* activeItem)
{
    for(vector<void*>::iterator i = items.begin(); i != items.end(); i++)
    {
        bool isActive = (*i == activeItem);
        if(isActive)
            attron(COLOR_PAIR(1)); 
            
        const char* data = printFunc(*i);
        mvwprintw(stdscr, starty++, (COLS - strlen(data)) / 2, "%s", data);
        
        if(isActive)
            attroff(COLOR_PAIR(1));
    }
}

static void* showListBox(vector<void*>& items, const char* printFunc(void*), vector<void*>::iterator& activeItem)
{
    erase();    
             
    while(1)
    {
        drawTitle();
        drawListBox(items, printFunc, 2, *activeItem);
        
        char c = ::getch();
        switch(c)
        {
            case 27:
            case 'q':
                return NULL;
            case 10:
                return *activeItem;
            case (char)KEY_DOWN:
                if(activeItem + 1 != items.end())
                    activeItem++;
                break;
            case (char)KEY_UP:
                if(activeItem != items.begin())
                    activeItem--;
                break;
        }
    }
    refresh();
}

static void showPost(FreshNet& freshnet, Post& post)
{
    erase();
    drawTitle();
    
    WINDOW* childwin = subwin(stdscr, LINES - 4, COLS - 4, 2, 2);
    
    mvwaddstr(childwin, 0, 0, post.getContentStripped().c_str());
    refresh();
    //wrefresh(childwin);
           
    char c = getch();
    if(c == 10)
    {
        freshnet.visitLink(post.link);
        wrefresh(stdscr);
    }
        
    delwin(childwin);
}

static void showPosts(FreshNet& freshnet, Feed& feed)
{
    mvwprintw(stdscr, 3, 3, feed.getTitle().c_str());
    vector<Post> posts = feed.getPosts();
    vector<void*> items;
    for(vector<Post>::iterator i = posts.begin(); i != posts.end(); i++)
        items.push_back(&(*i));
        
    vector<void*>::iterator active = items.begin();
    while(1)
    {
        void *ptr = showListBox(items, printPost, active);
        if(ptr == NULL)
            return;
        else
            showPost(freshnet, *((Post*)ptr));
    }
}

static void showFeeds(FreshNet& freshnet)
{    
    vector<Feed> feeds = freshnet.getFeeds();
    vector<void*> items;
    for(vector<Feed>::iterator i = feeds.begin(); i != feeds.end(); i++)
        items.push_back(&(*i));
    
    vector<void*>::iterator active = items.begin();    
    while(1)
    {
        void *ptr = showListBox(items, printFeed, active);
        if(ptr == NULL)
            return;
        else
            showPosts(freshnet, *((Feed*)ptr));
    }
}

static void showUI(FreshNet& freshnet)
{    
   
    WINDOW* win;
    win = initscr();
    cbreak();
    keypad(stdscr, TRUE);
    noecho();

    start_color();
    init_pair(1, COLOR_BLACK, COLOR_WHITE);
    init_pair(2, COLOR_WHITE, COLOR_BLACK);
    init_pair(3, COLOR_CYAN, COLOR_BLACK);
        
    showFeeds(freshnet);
    
    delwin(win);
    endwin();
    
}

int main(int argc, char **argv)
{
    string database = string(getenv("HOME")) + "/.freshnet.db3";
    map < Parameter, string > params;

    for (int i = 1; i < argc; i++) {
	string param = argv[i];
	if (param == "-d" || param == "--database") {
	    assertExtraParameter(argc, i);
	 // params[PARAM_DATABASE] = argv[i++];
	    database = argv[++i];
	} else if (param == "-a" || param == "--add") {
	    assertExtraParameter(argc, i);
	    params[PARAM_ADD] = argv[++i];
	} else if (param == "-r" || param == "--remove") {
	    assertExtraParameter(argc, i);
	    params[PARAM_REMOVE] = argv[++i];
    } else if (param == "-l" || param == "--list") {
        params[PARAM_LIST] = "true";
	} else if (param == "-p" || param == "--posts") {
        params[PARAM_POSTS] = "true";
    } else if (param == "-u" || param == "--update") {
        params[PARAM_UPDATE] = "true";    
    } else {
	    FreshNet::printUsage();
	    return 1;
	}
    }
    
    if (params.find(PARAM_DATABASE) != params.end())
	database = params[PARAM_DATABASE];

    FreshNet freshnet(database);

    if (params.find(PARAM_ADD) != params.end()) {
	   addFeed(params[PARAM_ADD], freshnet);
    }
    
    if(params.find(PARAM_POSTS) != params.end()){
        vector<Feed> feeds = freshnet.getFeeds();
        for(vector<Feed>::iterator i = feeds.begin(); i != feeds.end(); i++)
        {
            cout << (*i).getTitle() << " - " << (*i).getFeedUrl() << endl;
            vector<Post> posts = (*i).getPosts();
            for(vector<Post>::iterator p = posts.begin(); p != posts.end(); p++)
                cout << "\t" << (*p).title << endl;
        }
    }
    
    if(params.find(PARAM_LIST) != params.end()){
        vector<Feed> feeds = freshnet.getFeeds();
        for(vector<Feed>::iterator i = feeds.begin(); i != feeds.end(); i++)
        {
            cout << (*i).getTitle() << " - " << (*i).getFeedUrl() << endl;
        }
    }

    if (params.find(PARAM_REMOVE) != params.end()) {
        removeFeed(params[PARAM_REMOVE], freshnet);
    }
    
    vector<Feed> feeds = freshnet.getFeeds();
    
    if(params.find(PARAM_UPDATE) != params.end()) {
        params.erase(PARAM_UPDATE);
        for(vector<Feed>::iterator i = feeds.begin(); i != feeds.end(); i++)
            addFeed((*i).getFeedUrl(), freshnet, false);
    }

    if(feeds.size() == 0)
        cout << "No feeds have been added to FreshNet.\nYou can add a feed with -a <url> or --add <url>\n\n";
    else if (params.size() == 0)
    	showUI(freshnet);
}
